/*
    SEL - Simple DirectMedia Layer Extension Library
    Copyright (C) 2002 Matej Knopp <knopp@users.sf.net>

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Library General Public
    License as published by the Free Software Foundation; either
    version 2 of the License, or (at your option) any later version.

    This library is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    Library General Public License for more details.

    You should have received a copy of the GNU Library General Public
    License along with this library; if not, write to the Free
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/

#include "painter.h"
#include "../base/surface.h"
#include "../base/error.h"

namespace SEL
{

///////////////////////////////////////////////////////////////////////
///////////// EMPTY (to avoid crashing) drawing functions /////////////
///////////////////////////////////////////////////////////////////////

static void
putPixelInvalid (Surface *, Uint16, Uint16, Uint32)
{
}

static void
drawHLineInvalid (Surface *, Uint16, Uint16, Uint16, Uint32)
{
}

static void
drawVLineInvalid (Surface *, Uint16, Uint16, Uint16, Uint32)
{
}

static void
drawLineInvalid (Surface *, Uint16, Uint16, Uint16, Uint16, Uint32)
{
}

GFXTable GFXInvalid  = { putPixelInvalid, drawHLineInvalid, drawVLineInvalid, drawLineInvalid  };

#define __SET_PIXEL(bpp, pixelData, color) \
	*(Uint##bpp*)pixelData = color;

// anyone knows about something faster?
#if SDL_BYTEORDER == SDL_BIG_ENDIAN
#  define __SET_PIXEL24(data, b1, b2, b3) \
	data [0] = (b3); data [1] = (b2); data[2] = (b1);
#else
#  define __SET_PIXEL24(data, b1, b2, b3) \
	data [0] = (b1); data[1] = (b2); data[2] = (b3);
#endif		

///////////////////////////////////////////////////////////////////////
///////////// SOLID (not alpha blended) drawing functions /////////////
///////////////////////////////////////////////////////////////////////

static void 
putPixelSolid8 (Surface *surface, Uint16 x, Uint16 y, Uint32 color)
{
	Uint8 *data = (Uint8*) surface->pixels () + y * surface->pitch () + x;
	__SET_PIXEL (8, data, color);
}

static void 
putPixelSolid16 (Surface *surface, Uint16 x, Uint16 y, Uint32 color)
{
	Uint8 *data = 
		(Uint8*) surface->pixels () + y * surface->pitch () + 2 * x;
	__SET_PIXEL (16, data, color);
}

static void 
putPixelSolid24 (Surface *surface, Uint16 x, Uint16 y, Uint32 color)
{
	Uint8 *data = 
		(Uint8*) surface->pixels () + y * surface->pitch () + 3 * x;

	__SET_PIXEL24 (data, color & 0xFF, (color >> 8) & 0xFF,
		       (color >> 16) & 0xFF);
}

static void 
putPixelSolid32 (Surface *surface, Uint16 x, Uint16 y, Uint32 color)
{
	Uint8 *data = 
		(Uint8*) surface->pixels () + y * surface->pitch () + 4 * x;
	__SET_PIXEL (32, data, color);
}

static void
drawHLineSolid8 (Surface *surface, Uint16 x, Uint16 y, Uint16 w, Uint32 color)
{
	Uint8 *data = 
		(Uint8*) surface->pixels () + y * surface->pitch () + x;

	while (w > 0)
	{
	  	__SET_PIXEL (8, data, color);
		++data;
		--w;
	}		
}

static void
drawHLineSolid16 (Surface *surface, Uint16 x, Uint16 y, Uint16 w, Uint32 color)
{
	Uint16 *data = (Uint16*)
		((Uint8*) surface->pixels () + y * surface->pitch () + 2 * x);

	while (w > 0)
	{
	  	__SET_PIXEL (16, data, color);
		++data;
		--w;
	}		
}

static void
drawHLineSolid24 (Surface *surface, Uint16 x, Uint16 y, Uint16 w, Uint32 color)
{
	Uint8 *data = 
		(Uint8*) surface->pixels () + y * surface->pitch () + 3 * x;
	

	Uint8 c1 = color & 0xFF;
	Uint8 c2 = (color >> 8) & 0xFF;	
	Uint8 c3 = (color >> 16) & 0xFF;	

	while (w > 0)
	{

		__SET_PIXEL24 (data, c1, c2, c3);
		data += 3;
		--w;
	}		
}

static void
drawHLineSolid32 (Surface *surface, Uint16 x, Uint16 y, Uint16 w, Uint32 color)
{
	Uint32 *data = (Uint32*)
		((Uint8*) surface->pixels () + y * surface->pitch () + 4 * x);

	while (w > 0)
	{
	  	__SET_PIXEL (32, data, color);
		++data;
		--w;
	}		
}

static void
drawVLineSolid8 (Surface *surface, Uint16 x, Uint16 y, Uint16 h, Uint32 color)
{
	Uint16 pitch = surface->pitch ();

	Uint8 *data = 
		(Uint8*) surface->pixels () + (y * pitch) + x;

	while (h > 0)
	{
	  	__SET_PIXEL (8, data, color);

		data += pitch;
		--h;
	}		
}

static void
drawVLineSolid16 (Surface *surface, Uint16 x, Uint16 y, Uint16 h, Uint32 color)
{
	Uint16 pitch = surface->pitch ();

	Uint8 *data = 
		(Uint8*) surface->pixels () + (y * pitch) + (2 * x);

	while (h > 0)
	{
	  	__SET_PIXEL (16, data, color);
		data += pitch;
		--h;
	}		
}

static void
drawVLineSolid24 (Surface *surface, Uint16 x, Uint16 y, Uint16 h, Uint32 color)
{
	Uint16 pitch = surface->pitch ();

	Uint8 *data = 
		(Uint8*) surface->pixels () + (y * pitch) + (3 * x);

	Uint8 c1 = color & 0xFF;
	Uint8 c2 = (color >> 8) & 0xFF;	
	Uint8 c3 = (color >> 16) & 0xFF;	

	while (h > 0)
	{		
		__SET_PIXEL24 (data, c1, c2, c3);
		data += pitch;
		--h;
	}		
}

static void
drawVLineSolid32 (Surface *surface, Uint16 x, Uint16 y, Uint16 h, Uint32 color)
{
	Uint16 pitch = surface->pitch ();
	
	Uint8 *data = 
		(Uint8*) surface->pixels () + (y * pitch) + (4 * x);	

	while (h > 0)
	{
	  	__SET_PIXEL (32, data, color);
		data += pitch;
		--h;
	}		
}

// Line drawing algorithm (Bresenham)

#define __DO_LINE(x1, y1, x2, y2, command, surface) \
{ \
	Sint16 dx = x2 - x1; \
	Sint16 dy = y2 - y1; \
	Uint16 x = 0; \
	Uint16 y = 0; \
	Uint8 *data = \
		(Uint8*) surface->pixels () + y1 * surface->pitch () + x1 * surface->format ()->BytesPerPixel; \
	Sint16 d_pitch = (dy < 0) ? -surface->pitch () : surface->pitch (); \
	Sint16 d_data = (dx < 0) ? -surface->format ()->BytesPerPixel : surface->format ()->BytesPerPixel; \
	dx = abs (dx); \
	dy = abs (dy); \
	\
	if (dx >= dy) \
	{ \
		for (x = 0; x <= dx; x++) \
		{ \
			{ command; } \
			\
			y += dy; \
			if (y >= dx) \
			{ \
				y -= dx; \
				data += d_pitch; \
    			} \
			\
			data += d_data; \
		} \
	} \
	else \
	{ \
		for (y = 0; y <= dy; y++) \
		{ \
			{ command; } \
			\
			x += dx; \
			if (x >= dy) \
			{ \
				x -= dy; \
				data += d_data; \
			} \
			data += d_pitch; \
		} \
	} \
}

static void
drawLineSolid8 (Surface *surface, Uint16 x1, Uint16 y1, Uint16 x2, Uint16 y2,
	 	Uint32 color)
{
	__DO_LINE (x1, y1, x2, y2, __SET_PIXEL (8, data, color), surface);
}		

static void
drawLineSolid16 (Surface *surface, Uint16 x1, Uint16 y1, Uint16 x2, Uint16 y2,
	 	 Uint32 color)
{
	__DO_LINE (x1, y1, x2, y2, __SET_PIXEL (16, data, color), surface);
}

static void
drawLineSolid24 (Surface *surface, Uint16 x1, Uint16 y1, Uint16 x2, Uint16 y2,
	 	 Uint32 color)
{
	Uint8 c1 = color & 0xFF;
	Uint8 c2 = (color >> 8) & 0xFF;	
	Uint8 c3 = (color >> 16) & 0xFF;	

	__DO_LINE (x1, y1, x2, y2, __SET_PIXEL24 (data, c1, c2, c3), surface);
}

static void
drawLineSolid32 (Surface *surface, Uint16 x1, Uint16 y1, Uint16 x2, Uint16 y2,
	 	 Uint32 color)
{
	__DO_LINE (x1, y1, x2, y2, __SET_PIXEL (32, data, color), surface);
}

#define __MAKE_LOCKED_PUT_PIXEL(type) \
static void \
putPixel##type##L (Surface *surface, Uint16 x, Uint16 y, Uint32 color) \
{ \
	surface->lock (); \
	putPixel##type (surface, x, y, color); \
	surface->unlock (); \
}

#define __MAKE_LOCKED_H_LINE(type) \
static void \
drawHLine##type##L (Surface *surface, Uint16 x, Uint16 y, Uint16 w, Uint32 color) \
{ \
	surface->lock (); \
	drawHLine##type (surface, x, y, w, color); \
	surface->unlock (); \
}

#define __MAKE_LOCKED_V_LINE(type) \
static void \
drawVLine##type##L (Surface *surface, Uint16 x, Uint16 y, Uint16 h, Uint32 color) \
{ \
	surface->lock (); \
	drawVLine##type (surface, x, y, h, color); \
	surface->unlock (); \
}

#define __MAKE_LOCKED_LINE(type) \
static void \
drawLine##type##L (Surface *surface, Uint16 x, Uint16 y, Uint16 x2, Uint16 y2, Uint32 color) \
{ \
	surface->lock (); \
	drawLine##type (surface, x, y, x2, y2, color); \
	surface->unlock (); \
}

__MAKE_LOCKED_PUT_PIXEL (Solid8);
__MAKE_LOCKED_H_LINE    (Solid8);
__MAKE_LOCKED_V_LINE    (Solid8);
__MAKE_LOCKED_LINE      (Solid8);

__MAKE_LOCKED_PUT_PIXEL (Solid16);
__MAKE_LOCKED_H_LINE    (Solid16);
__MAKE_LOCKED_V_LINE    (Solid16);
__MAKE_LOCKED_LINE      (Solid16);

__MAKE_LOCKED_PUT_PIXEL (Solid24);
__MAKE_LOCKED_H_LINE    (Solid24);
__MAKE_LOCKED_V_LINE    (Solid24);
__MAKE_LOCKED_LINE      (Solid24);

__MAKE_LOCKED_PUT_PIXEL (Solid32);
__MAKE_LOCKED_H_LINE    (Solid32);
__MAKE_LOCKED_V_LINE    (Solid32);
__MAKE_LOCKED_LINE      (Solid32);

//////////////////////////////////////////////////////////////////////////
////////// ALPHA BLENDED (transparency aware) drawing functions //////////
//////////////////////////////////////////////////////////////////////////

// macros for color manipulation (some are taken from SDL)

#define __RGBA_FROM_RGBA8888(pixel, r, g, b, a)				\
{									\
	r = (pixel>>24);						\
	g = ((pixel>>16)&0xFF);						\
	b = ((pixel>>8)&0xFF);						\
	a = (pixel&0xFF);						\
}

#define __RGB_FROM_RGB565(pixel, r, g, b)				\
{									\
	r = (((pixel&0xF800)>>11)<<3);		 			\
	g = (((pixel&0x07E0)>>5)<<2); 					\
	b = ((pixel&0x001F)<<3); 					\
}

#define __RGB_FROM_RGB888(pixel, r, g, b)				\
{									\
	r = ((pixel&0xFF0000)>>16);		 			\
	g = ((pixel&0xFF00)>>8);		 			\
	b = (pixel&0xFF);			 			\
}

#define __RGB565_FROM_RGB(pixel, r, g, b)				\
{									\
	pixel = ((r>>3)<<11)|((g>>2)<<5)|(b>>3);			\
}

#define __RGB888_FROM_RGB(pixel, r, g, b)				\
{									\
	pixel = (r<<16)|(g<<8)|b;					\
}

// Blend the RGB values of two pixels based on a source alpha value */
#define __ALPHA_BLEND(sR, sG, sB, A, dR, dG, dB)  \
do {						  \
	dR = ((((sR-dR)*(A))>>8)+dR) & 0xFF;	  \
	dG = ((((sG-dG)*(A))>>8)+dG) & 0xFF;	  \
	dB = ((((sB-dB)*(A))>>8)+dB) & 0xFF;	  \
} while(0)

// this is in fact 128 (~127)
#define __ALPHA_HALF 127 

// Blend the RGB values two pixel with alpha = 128 (~127) - optimized
#define __ALPHA_BLEND_HALF(sR, sG, sB, dR, dG, dB)   \
do { 						     \
	dR = ((sR + dR) >> 1);			     \
	dG = ((sG + dG) >> 1);			     \
	dB = ((sB + dB) >> 1);			     \
} while (0)

#define __ALPHA_BLEND_AUTO(sR, sG, sB, A, dR, dG, dB) 	     \
{ 						      	     \
	if (a == 127) 					     \
	{						     \
		__ALPHA_BLEND_HALF (sR, sG, sB, dR, dG, dB); \
	}						     \
	else						     \
	{						     \
		__ALPHA_BLEND (sR, sG, sB, A, dR, dG, dB);   \
	}						     \
}

static void 
putPixelBlend16 (Surface *surface, Uint16 x, Uint16 y, Uint32 color)
{
	Uint8 dr, dg, db, a;

	__RGBA_FROM_RGBA8888 (color, dr, dg, db, a);
    
	if (a == 0)
		return;	

	Uint16 *data = (Uint16*)
		((Uint8*) surface->pixels () + y * surface->pitch () + 2 * x);

	if (a == 255) 
	{
		__RGB565_FROM_RGB (*data, dr, dg, db);
		return;
	}

	Uint8 sr, sg, sb;
    
	__RGB_FROM_RGB565 (*data, sr, sg, sb);

	a = ~a;
	
	__ALPHA_BLEND_AUTO (sr, sg, sb, a, dr, dg, db);
	__RGB565_FROM_RGB (*data, dr, dg, db);
}

static void 
putPixelBlend24 (Surface *surface, Uint16 x, Uint16 y, Uint32 color)
{
	Uint8 dr, dg, db, a;

	__RGBA_FROM_RGBA8888 (color, dr, dg, db, a);
    
	if (a == 0)
		return;	

	Uint32 *data = (Uint32*)
		((Uint8*) surface->pixels () + y * surface->pitch () + 3 * x);

	if (a == 255) 
	{
		__SET_PIXEL24 (((Uint8*)data), db, dg, dr);
		return;
	}

	Uint8 sr, sg, sb;
    
	__RGB_FROM_RGB888 (*data, sr, sg, sb);

	a = ~a;
	
	__ALPHA_BLEND_AUTO (sr, sg, sb, a, dr, dg, db);
	__SET_PIXEL24 (((Uint8*)data), db, dg, dr);
	
}


static void 
putPixelBlend32 (Surface *surface, Uint16 x, Uint16 y, Uint32 color)
{
	Uint8 dr, dg, db, a;

	__RGBA_FROM_RGBA8888 (color, dr, dg, db, a);
    
	if (a == 0)
		return;	

	Uint32 *data = (Uint32*)
		((Uint8*) surface->pixels () + y * surface->pitch () + 4 * x);

	if (a == 255) 
	{
		__RGB888_FROM_RGB (*data, dr, dg, db);
		return;
	}

	Uint8 sr, sg, sb;
    
	__RGB_FROM_RGB888 (*data, sr, sg, sb);

	a = ~a;
	
	__ALPHA_BLEND_AUTO (sr, sg, sb, a, dr, dg, db);
	__RGB888_FROM_RGB (*data, dr, dg, db);
}


static void 
drawHLineBlend16 (Surface *surface, Uint16 x, Uint16 y, Uint16 w, Uint32 color)
{
	Uint8 dr, dg, db, a;

	__RGBA_FROM_RGBA8888 (color, dr, dg, db, a);
    
	if (a == 0)
		return;							

	if (a == 255) 
	{    
		__RGB565_FROM_RGB (color, dr, dg, db);
		drawHLineSolid16 (surface, x, y, w, color);
		return;
	}

	Uint16 *data = (Uint16*)
		((Uint8*) surface->pixels () + y * surface->pitch () + 2 * x);

	Uint8 sr, sg, sb;
	Uint8 dr_, dg_, db_;
    
	a = ~a;

	if (a == __ALPHA_HALF)
	{
		while (w > 0)
		{
			__RGB_FROM_RGB565 (*data, sr, sg, sb);
			dr_ = dr; dg_ = dg; db_ = db;
			__ALPHA_BLEND_HALF (sr, sg, sb, dr_, dg_, db_);
			__RGB565_FROM_RGB (*data, dr_, dg_, db_);
			++data;
			--w;
		}
	}		
	else
	{
		while (w > 0)
		{
			__RGB_FROM_RGB565 (*data, sr, sg, sb);
			dr_ = dr; dg_ = dg; db_ = db;
			__ALPHA_BLEND (sr, sg, sb, a, dr_, dg_, db_);
			__RGB565_FROM_RGB (*data, dr_, dg_, db_);
			++data;
			--w;
		}
	}		
}

static void 
drawHLineBlend24 (Surface *surface, Uint16 x, Uint16 y, Uint16 w, Uint32 color)
{
	Uint8 dr, dg, db, a;

	__RGBA_FROM_RGBA8888 (color, dr, dg, db, a);
    
	if (a == 0)
		return;							

	if (a == 255) 
	{    
		__RGB888_FROM_RGB (color, dr, dg, db);
		drawHLineSolid24 (surface, x, y, w, color);
		return;
	}

	Uint32 *data = (Uint32*)
		((Uint8*) surface->pixels () + y * surface->pitch () + 3 * x);

	Uint8 sr, sg, sb;
	Uint8 dr_, dg_, db_;
    
	a = ~a;

	if (a == __ALPHA_HALF)
	{
		while (w > 0)
		{
			__RGB_FROM_RGB888 (*data, sr, sg, sb);
			dr_ = dr; dg_ = dg; db_ = db;
			__ALPHA_BLEND_HALF (sr, sg, sb, dr_, dg_, db_);			
			__SET_PIXEL24 (((Uint8*)data), db_, dg_, dr_);
// TODO: 	((Uint8*)data) += 3;
			data += 3;
			--w;
		}
	}		
	else
	{
		while (w > 0)
		{
			__RGB_FROM_RGB888 (*data, sr, sg, sb);
			dr_ = dr; dg_ = dg; db_ = db;
			__ALPHA_BLEND (sr, sg, sb, a, dr_, dg_, db_);
			__SET_PIXEL24 (((Uint8*)data), db_, dg_, dr_);
// TODO: 	((Uint8*)data) += 3;
			data += 3;
			--w;
		}
	}		
}

static void 
drawHLineBlend32 (Surface *surface, Uint16 x, Uint16 y, Uint16 w, Uint32 color)
{
	Uint8 dr, dg, db, a;

	__RGBA_FROM_RGBA8888 (color, dr, dg, db, a);
    
	if (a == 0)
		return;							

	if (a == 255) 
	{    
		__RGB888_FROM_RGB (color, dr, dg, db);
		drawHLineSolid32 (surface, x, y, w, color);
		return;
	}

	Uint32 *data = (Uint32*)
		((Uint8*) surface->pixels () + y * surface->pitch () + 4 * x);

	Uint8 sr, sg, sb;
	Uint8 dr_, dg_, db_;
    
	a = ~a;

	if (a == __ALPHA_HALF)
	{
		while (w > 0)
		{
			__RGB_FROM_RGB888 (*data, sr, sg, sb);
			dr_ = dr; dg_ = dg; db_ = db;
			__ALPHA_BLEND_HALF (sr, sg, sb, dr_, dg_, db_);
			__RGB888_FROM_RGB (*data, dr_, dg_, db_);
			++data;
			--w;
		}
	}		
	else
	{
		while (w > 0)
		{
			__RGB_FROM_RGB888 (*data, sr, sg, sb);
			dr_ = dr; dg_ = dg; db_ = db;
			__ALPHA_BLEND (sr, sg, sb, a, dr_, dg_, db_);
			__RGB888_FROM_RGB (*data, dr_, dg_, db_);
			++data;
			--w;
		}
	}		
}

static void 
drawVLineBlend16 (Surface *surface, Uint16 x, Uint16 y, Uint16 h, Uint32 color)
{
	Uint8 dr, dg, db, a;

	__RGBA_FROM_RGBA8888 (color, dr, dg, db, a);
    
	if (a == 0)
		return;							

	if (a == 255) 
	{    
		__RGB565_FROM_RGB (color, dr, dg, db);
		drawVLineSolid16 (surface, x, y, h, color);
		return;
	}

	Uint16 pitch = surface->pitch ();

	Uint16 *data = (Uint16*)
		((Uint8*) surface->pixels () + (y * pitch) + (2 * x));

	Uint8 sr, sg, sb;
	Uint8 dr_, dg_, db_;
    
	a = ~a;

	if (a == __ALPHA_HALF)
	{
		while (h > 0)
		{
			__RGB_FROM_RGB565 (*data, sr, sg, sb);
			dr_ = dr; dg_ = dg; db_ = db;
			__ALPHA_BLEND_HALF (sr, sg, sb, dr_, dg_, db_);
			__RGB565_FROM_RGB (*data, dr_, dg_, db_);
// TODO:    ((Uint8*)data) += pitch;
			data += pitch;
			--h;
		}
	}		
	else
	{
		while (h > 0)
		{
			__RGB_FROM_RGB565 (*data, sr, sg, sb);
			dr_ = dr; dg_ = dg; db_ = db;
			__ALPHA_BLEND (sr, sg, sb, a, dr_, dg_, db_);
			__RGB565_FROM_RGB (*data, dr_, dg_, db_);
// TODO:    ((Uint8*)data) += pitch;
			data += pitch;
			--h;			
		}
	}		
}

static void 
drawVLineBlend24 (Surface *surface, Uint16 x, Uint16 y, Uint16 h, Uint32 color)
{
	Uint8 dr, dg, db, a;

	__RGBA_FROM_RGBA8888 (color, dr, dg, db, a);
    
	if (a == 0)
		return;							

	if (a == 255) 
	{    
		__RGB888_FROM_RGB (color, dr, dg, db);
		drawVLineSolid24 (surface, x, y, h, color);
		return;
	}

	Uint16 pitch = surface->pitch ();

	Uint32 *data = (Uint32*)
		((Uint8*) surface->pixels () + (y * pitch) + (3 * x));

	Uint8 sr, sg, sb;
	Uint8 dr_, dg_, db_;
    
	a = ~a;

	if (a == __ALPHA_HALF)
	{
		while (h > 0)
		{
			__RGB_FROM_RGB888 (*data, sr, sg, sb);
			dr_ = dr; dg_ = dg; db_ = db;
			__ALPHA_BLEND_HALF (sr, sg, sb, dr_, dg_, db_);
			__SET_PIXEL24 (((Uint8*)data), db_, dg_, dr_);
// TODO:    ((Uint8*)data) += pitch;
			data += pitch;
			--h;
		}
	}		
	else
	{
		while (h > 0)
		{
			__RGB_FROM_RGB888 (*data, sr, sg, sb);
			dr_ = dr; dg_ = dg; db_ = db;
			__ALPHA_BLEND (sr, sg, sb, a, dr_, dg_, db_);
			__SET_PIXEL24 (((Uint8*)data), db_, dg_, dr_);
// TODO:    ((Uint8*)data) += pitch;
			data += pitch;
			--h;			
		}
	}		
}


static void 
drawVLineBlend32 (Surface *surface, Uint16 x, Uint16 y, Uint16 h, Uint32 color)
{
	Uint8 dr, dg, db, a;

	__RGBA_FROM_RGBA8888 (color, dr, dg, db, a);
    
	if (a == 0)
		return;							

	if (a == 255) 
	{    
		__RGB888_FROM_RGB (color, dr, dg, db);
		drawVLineSolid32 (surface, x, y, h, color);
		return;
	}

	Uint16 pitch = surface->pitch ();

	Uint32 *data = (Uint32*)
		((Uint8*) surface->pixels () + (y * pitch) + (4 * x));

	Uint8 sr, sg, sb;
	Uint8 dr_, dg_, db_;
    
	a = ~a;

	if (a == __ALPHA_HALF)
	{
		while (h > 0)
		{
			__RGB_FROM_RGB888 (*data, sr, sg, sb);
			dr_ = dr; dg_ = dg; db_ = db;
			__ALPHA_BLEND_HALF (sr, sg, sb, dr_, dg_, db_);
			__RGB888_FROM_RGB (*data, dr_, dg_, db_);
// TODO:    ((Uint8*)data) += pitch;
			data += pitch;
			--h;
		}
	}		
	else
	{
		while (h > 0)
		{
			__RGB_FROM_RGB888 (*data, sr, sg, sb);
			dr_ = dr; dg_ = dg; db_ = db;
			__ALPHA_BLEND (sr, sg, sb, a, dr_, dg_, db_);
			__RGB888_FROM_RGB (*data, dr_, dg_, db_);
// TODO:    ((Uint8*)data) += pitch;
			data += pitch;
			--h;			
		}
	}		
}

static void
drawLineBlend16 (Surface *surface, Uint16 x1, Uint16 y1, Uint16 x2, Uint16 y2,
	 	 Uint32 color)
{
	Uint8 dr, dg, db, a;

	__RGBA_FROM_RGBA8888 (color, dr, dg, db, a);
    
	if (a == 0)
		return;							

	if (a == 255) 
	{    
		__RGB565_FROM_RGB (color, dr, dg, db);
		drawLineSolid16 (surface, x1, y1, x2, y2, color);
		return;
	}

	Uint8 sr, sg, sb;
	Uint8 dr_, dg_, db_;
    
	a = ~a;

	Uint16 *data_;

	if (a == __ALPHA_HALF)
	{		
		__DO_LINE (x1, y1, x2, y2, 
			    data_ = (Uint16*)data;
			    __RGB_FROM_RGB565 (*data_, sr, sg, sb); 
			    dr_ = dr; dg_= dg; db_ = db; 
			    __ALPHA_BLEND_HALF (sr, sg, sb, dr_, dg_, db_); 
			    __RGB565_FROM_RGB (*data_, dr_, dg_, db_),
			   surface);
	}		
	else
	{
		__DO_LINE (x1, y1, x2, y2, 
			    data_ = (Uint16*)data;		
			    __RGB_FROM_RGB565 (*data_, sr, sg, sb); 
			    dr_ = dr; dg_= dg; db_ = db; 
			    __ALPHA_BLEND (sr, sg, sb, a, dr_, dg_, db_); 
			    __RGB565_FROM_RGB (*data_, dr_, dg_, db_),
			   surface);
	}		
}

static void
drawLineBlend24 (Surface *surface, Uint16 x1, Uint16 y1, Uint16 x2, Uint16 y2,
	 	 Uint32 color)
{
	Uint8 dr, dg, db, a;

	__RGBA_FROM_RGBA8888 (color, dr, dg, db, a);
    
	if (a == 0)
		return;							

	if (a == 255) 
	{    
		__RGB888_FROM_RGB (color, dr, dg, db);
		drawLineSolid24 (surface, x1, y1, x2, y2, color);
		return;
	}

	Uint8 sr, sg, sb;
	Uint8 dr_, dg_, db_;
    
	a = ~a;

	Uint32 *data_;

	if (a == __ALPHA_HALF)
	{		
		__DO_LINE (x1, y1, x2, y2, 
			    data_ = (Uint32*)data;
			    __RGB_FROM_RGB888 (*data_, sr, sg, sb); 
			    dr_ = dr; dg_= dg; db_ = db; 
			    __ALPHA_BLEND_HALF (sr, sg, sb, dr_, dg_, db_); 
			    __SET_PIXEL24 (data, db_, dg_, dr_),
			   surface);
	}		
	else
	{
		__DO_LINE (x1, y1, x2, y2, 
			    data_ = (Uint32*)data;		
			    __RGB_FROM_RGB888 (*data_, sr, sg, sb); 
			    dr_ = dr; dg_= dg; db_ = db; 
			    __ALPHA_BLEND (sr, sg, sb, a, dr_, dg_, db_); 
			    __SET_PIXEL24 (data, db_, dg_, dr_),
			   surface);
	}		
}

static void
drawLineBlend32 (Surface *surface, Uint16 x1, Uint16 y1, Uint16 x2, Uint16 y2,
	 	 Uint32 color)
{
	Uint8 dr, dg, db, a;

	__RGBA_FROM_RGBA8888 (color, dr, dg, db, a);
    
	if (a == 0)
		return;							

	if (a == 255) 
	{    
		__RGB888_FROM_RGB (color, dr, dg, db);
		drawLineSolid32 (surface, x1, y1, x2, y2, color);
		return;
	}

	Uint8 sr, sg, sb;
	Uint8 dr_, dg_, db_;
    
	a = ~a;

	Uint32 *data_;

	if (a == __ALPHA_HALF)
	{		
		__DO_LINE (x1, y1, x2, y2, 
			    data_ = (Uint32*)data;
			    __RGB_FROM_RGB888 (*data_, sr, sg, sb); 
			    dr_ = dr; dg_= dg; db_ = db; 
			    __ALPHA_BLEND_HALF (sr, sg, sb, dr_, dg_, db_); 
			    __RGB888_FROM_RGB (*data_, dr_, dg_, db_),
			   surface);
	}		
	else
	{
		__DO_LINE (x1, y1, x2, y2, 
			    data_ = (Uint32*)data;		
			    __RGB_FROM_RGB888 (*data_, sr, sg, sb); 
			    dr_ = dr; dg_= dg; db_ = db; 
			    __ALPHA_BLEND (sr, sg, sb, a, dr_, dg_, db_); 
			    __RGB888_FROM_RGB (*data_, dr_, dg_, db_),
			   surface);
	}		
}

#undef __DO_LINE

__MAKE_LOCKED_PUT_PIXEL (Blend16);
__MAKE_LOCKED_H_LINE    (Blend16);
__MAKE_LOCKED_V_LINE    (Blend16);
__MAKE_LOCKED_LINE      (Blend16);

__MAKE_LOCKED_PUT_PIXEL (Blend24);
__MAKE_LOCKED_H_LINE    (Blend24);
__MAKE_LOCKED_V_LINE    (Blend24);
__MAKE_LOCKED_LINE      (Blend24);

__MAKE_LOCKED_PUT_PIXEL (Blend32);
__MAKE_LOCKED_H_LINE    (Blend32);
__MAKE_LOCKED_V_LINE    (Blend32);
__MAKE_LOCKED_LINE      (Blend32);

GFXTable Painter::m_gfx_tables [2][2][4] = 
{
  { 
    {
      { putPixelSolid8,  drawHLineSolid8,  drawVLineSolid8,  drawLineSolid8  },
      { putPixelSolid16, drawHLineSolid16, drawVLineSolid16, drawLineSolid16 },
      { putPixelSolid24, drawHLineSolid24, drawVLineSolid24, drawLineSolid24 },
      { putPixelSolid32, drawHLineSolid32, drawVLineSolid32, drawLineSolid32 }
    },
    {
      { putPixelSolid8L,  drawHLineSolid8L,  drawVLineSolid8L,  drawLineSolid8L  },
      { putPixelSolid16L, drawHLineSolid16L, drawVLineSolid16L, drawLineSolid16L },
      { putPixelSolid24L, drawHLineSolid24L, drawVLineSolid24L, drawLineSolid24L },
      { putPixelSolid32L, drawHLineSolid32L, drawVLineSolid32L, drawLineSolid32L }
    }
  },
  { 
    {
      { putPixelSolid8,  drawHLineSolid8,  drawVLineSolid8,  drawLineSolid8  },
      { putPixelBlend16, drawHLineBlend16, drawVLineBlend16, drawLineBlend16 },
      { putPixelBlend24, drawHLineBlend24, drawVLineBlend24, drawLineBlend24 },
      { putPixelBlend32, drawHLineBlend32, drawVLineBlend32, drawLineBlend32 }
    },
    {
      { putPixelSolid8L,  drawHLineSolid8L,  drawVLineSolid8L,  drawLineSolid8L  },
      { putPixelBlend16L, drawHLineBlend16L, drawVLineBlend16L, drawLineBlend16L },
      { putPixelBlend24L, drawHLineBlend24L, drawVLineBlend24L, drawLineBlend24L },
      { putPixelBlend32L, drawHLineBlend32L, drawVLineBlend32L, drawLineBlend32L }
    }
  }
};

#if 0 // this will not be included in the GFX table
// shamelessly taken from allegro...
static
void do_circle (Surface *surface, Uint16 x, Uint16 y, Uint16 radius, int d,
	        void (*proc)(Surface *, int, int, int))
{
	Sint16 cx = 0;
	Sint16 cy = radius;
	Sint16 df = 1 - radius; 
	Sint16 d_e = 3;
	Sint16 d_se = -2 * radius + 5;

	do {
		proc (surface, x+cx, y+cy, d);

		if (cx) 
			proc (surface, x-cx, y+cy, d); 

		if (cy) 
			proc (surface, x+cx, y-cy, d);

		if ((cx) && (cy)) 
			proc (surface, x-cx, y-cy, d); 

		if (cx != cy) 
		{
			proc (surface, x+cy, y+cx, d); 

			if (cx) 
				proc (surface, x+cy, y-cx, d);

			if (cy) 
				proc (surface, x-cy, y+cx, d); 

			if (cx && cy) 
				proc (surface, x-cy, y-cx, d); 
		}

		if (df < 0)
		{
			df += d_e;
			d_e += 2;
			d_se += 2;
		}
		else 
		{	 
			df += d_se;
			d_e += 2;
			d_se += 4;
			cy--;
		} 

		cx++; 

	} while (cx <= cy);
}
#endif // 0

/////////////////////////////////////////////////////////////////
/////////////////////// The Painter Class ///////////////////////
/////////////////////////////////////////////////////////////////

Painter::Painter (Surface *target, int mode, bool nothrow)
	: m_gfx (&GFXInvalid), m_target_surface (target), m_mode (mode), 
	  m_clipping (true), m_auto_lock (true)
{ 
	if (!target) 
	{	
		warning ("Painter: Cannot operate on NULL surface !");
		if (!nothrow)
			throw Error ("Couldn't create painter with NULL target surface");
	}
	else
	{		
	        updateGFXTable ();
		target->ref ();
	}
	resetClipRect ();
}

Painter::~Painter ()
{
        if (m_target_surface)
		m_target_surface->unref ();
}

bool
Painter::updateGFXTable ()
{
	m_gfx = &GFXInvalid;

	return_val_if_fail (m_target_surface != NULL, false);

	if (m_mode != ModeSolid && m_mode != ModeBlended)
	{
		warning ("Unknown drawing mode!");
		return false;
	}
	
	int depth = m_target_surface->format ()->BytesPerPixel;
	
	if (depth < 1 || depth > 4)
	{
		warning ("Unknown bit-depth of the target surface !");
		return false;
	}	    
	
	m_gfx = &m_gfx_tables [m_mode][m_auto_lock][depth - 1];

	return true;
}

void 
Painter::setMode (int mode)
{
	m_mode = mode;
	updateGFXTable ();
}

void
Painter::setTarget (Surface *target)
{
	releaseTarget ();
	m_target_surface = target;
	updateGFXTable ();
	resetClipRect ();
	if (target)
		target->ref ();
}

void
Painter::releaseTarget ()
{
	if (m_target_surface)
		m_target_surface->unref ();
	m_target_surface = NULL;
	m_gfx = NULL;
	m_clip_rect = Rect (0, 0, 0, 0);		
}

void
Painter::setClipping (bool clipping)
{
	m_clipping = clipping;
}

void
Painter::setAutoLock (bool auto_lock)
{
	m_auto_lock = auto_lock;
	updateGFXTable ();
}

void
Painter::setClipRect (const Rect & rect)
{
	m_clip_rect = rect;
	m_clip_x2 = rect.x + rect.w - 1;
	m_clip_y2 = rect.y + rect.h - 1;
}

void
Painter::retrieveClipRect ()
{
	if (!m_target_surface)
		setClipRect (Rect (0, 0, 0, 0));
	else
		setClipRect (m_target_surface->clipRect ());
}

void
Painter::resetClipRect ()
{
	if (!m_target_surface)
		setClipRect (Rect (0, 0, 0, 0));
	else
		setClipRect (Rect (0, 0, m_target_surface->w (),
					 m_target_surface->h ()));
}

void
Painter::putPixel (Uint16 x, Uint16 y, Uint32 color)
{
	if (m_clipping)
	{
		if (x < m_clip_rect.x || x > m_clip_x2 ||
		    y < m_clip_rect.y || y > m_clip_y2)
			return;
	}
	
	m_gfx->putPixel (m_target_surface, x, y, color);
}

void
Painter::drawHLine (Uint16 x, Uint16 y, Uint16 w, Uint32 color)
{
	if (w == 0)
		return;	

	if (m_clipping)
	{
		if (y < m_clip_rect.y || y > m_clip_y2)
			return;

		if ((x + w - 1) < m_clip_rect.x)
			return;

		if (x > m_clip_x2)
			return;

		if (x < m_clip_rect.x)
		{
			w -= (m_clip_rect.x - x);
			x = m_clip_rect.x;
		}
		
		if ((x + w) > (m_clip_x2 + 1))
		{
			w -= (w + x) - m_clip_x2 - 1;
		}
	}

	m_gfx->drawHLine (m_target_surface, x, y, w, color);
}

void
Painter::drawVLine (Uint16 x, Uint16 y, Uint16 h, Uint32 color)
{
	if (h == 0)
		return;

	if (m_clipping)
	{
		if (x < m_clip_rect.x || x > m_clip_x2)
			return;

		if ((y + h - 1) < m_clip_rect.y)
			return;

		if (y > m_clip_y2)
			return;

		if (y < m_clip_rect.y)
		{
			h -= (m_clip_rect.y - y);
			y = m_clip_rect.y;
		}
		
		if ((y + h) > (m_clip_y2 + 1))
		{
			h -= (h + y) - m_clip_y2 - 1;
		}
	}

	m_gfx->drawVLine (m_target_surface, x, y, h, color);
}

// This function was ripped from "Tricks of the Windows game programming gurus" 
// written by Andre Lamothe ISBN: 0-672-31361-8
// Then it was ripped from ClanLib and slightly adapted to work with SEL.
// It returns false if the line is totally outside the clipping area and true
// if the line was successfully clipped
static bool 
clipLine (Uint16 &x1, Uint16 &y1, Uint16 &x2, Uint16 &y2, Uint16 min_clip_x,
	  Uint16 max_clip_x, Uint16 min_clip_y, Uint16 max_clip_y)
{
// internal clipping codes
#define CLIP_CODE_C  0x0000
#define CLIP_CODE_N  0x0008
#define CLIP_CODE_S  0x0004
#define CLIP_CODE_E  0x0002
#define CLIP_CODE_W  0x0001

#define CLIP_CODE_NE 0x000a
#define CLIP_CODE_SE 0x0006
#define CLIP_CODE_NW 0x0009
#define CLIP_CODE_SW 0x0005

	Uint16 xc1 = x1;
	Uint16 yc1 = y1;
	Uint16 xc2 = x2;
	Uint16 yc2 = y2;

	int p1_code = 0;
	int p2_code = 0;

	// determine codes for p1 and p2
	if (y1 < min_clip_y)
	{
		p1_code |= CLIP_CODE_N;
	}
	else if (y1 > max_clip_y)
	{
		p1_code |= CLIP_CODE_S;
	}

	if (x1 < min_clip_x)
	{
		p1_code |= CLIP_CODE_W;
	}
	else if (x1 > max_clip_x)
	{
		p1_code |= CLIP_CODE_E;
	}

	if (y2 < min_clip_y)
	{
		p2_code |= CLIP_CODE_N;
	}
	else if (y2 > max_clip_y)
	{
		p2_code |= CLIP_CODE_S;
	}

	if (x2 < min_clip_x)
	{
		p2_code |= CLIP_CODE_W;
	}
	else if (x2 > max_clip_x)
	{
		p2_code |= CLIP_CODE_E;
	}

	// try and trivially reject
	if ((p1_code & p2_code))
	{
		return 0;
	}

	// test for totally visible, if so leave points untouched
	if (p1_code == 0 && p2_code == 0)
	{
		return 1;
	}

	// determine end clip point for p1
	switch (p1_code)
	{
	case CLIP_CODE_C: 
		break;

	case CLIP_CODE_N:		
		yc1 = (Uint16) min_clip_y;
		xc1 = (Uint16) (x1 + 0.5 + (min_clip_y - y1)
				* (x2 - x1) / (y2 - y1));
		break;

	case CLIP_CODE_S:
		yc1 = (Uint16) max_clip_y;
		xc1 = (Uint16) (x1 + 0.5 + (max_clip_y-y1) 
				* (x2 - x1) / (y2 - y1));
		break;

	case CLIP_CODE_W:
		xc1 = (Uint16) min_clip_x;
		yc1 = (Uint16) (y1 + 0.5 + (min_clip_x - x1)
				* (y2 - y1) / (x2 - x1));
		break;
		
	case CLIP_CODE_E:
		xc1 = (Uint16) max_clip_x;
		yc1 = (Uint16) (y1 + 0.5 + (max_clip_x - x1)
				* (y2 - y1) / (x2 - x1));
		break;

	// these cases are more complex, must compute 2 intersections
	case CLIP_CODE_NE:
		// north hline intersection
		yc1 = (Uint16) min_clip_y;
		xc1 = (Uint16) (x1 + 0.5 + (min_clip_y - y1)
				* (x2 - x1) / (y2 - y1));

		// test if intersection is valid, of so then done, else compute next
		if (xc1 < min_clip_x || xc1 > max_clip_x)
		{
				// east vline intersection
			xc1 = (Uint16) max_clip_x;
			yc1 = (Uint16) (y1 + 0.5 + (max_clip_x - x1)
					* (y2 - y1) / (x2 - x1));
		}
	
		break;
	
	case CLIP_CODE_SE:
		// south hline intersection
		yc1 = (Uint16) max_clip_y;
		xc1 = (Uint16) (x1 + 0.5 + (max_clip_y - y1)
				* (x2 - x1) / (y2 - y1));	

		// test if intersection is valid, of so then done, else compute next
		if (xc1 < min_clip_x || xc1 > max_clip_x)
		{
			// east vline intersection
			xc1 = (Uint16) max_clip_x;
			yc1 = (Uint16) (y1 + 0.5 + (max_clip_x - x1)
					* (y2 - y1) / (x2 - x1));
		}

		break;
	
	case CLIP_CODE_NW:
		// north hline intersection
		yc1 = (Uint16) min_clip_y;
		xc1 = (Uint16) (x1 + 0.5 + (min_clip_y - y1) 
				* (x2 - x1) / (y2 - y1));
			
		// test if intersection is valid, of so then done, else compute next
		if (xc1 < min_clip_x || xc1 > max_clip_x)
		{
			xc1 = (Uint16) min_clip_x;
			yc1 = (Uint16) (y1 + 0.5 + (min_clip_x - x1)
					* (y2 - y1) / (x2 - x1));	
		} 

		break;
	  	
	case CLIP_CODE_SW:
		// south hline intersection
		yc1 = (Uint16) max_clip_y;
		xc1 = (Uint16) (x1 + 0.5 + (max_clip_y - y1) 
				* (x2-x1)/(y2-y1));	

		// test if intersection is valid, of so then done, else compute next
		if (xc1 < min_clip_x || xc1 > max_clip_x)
		{
			xc1 = (Uint16) min_clip_x;
			yc1 = (Uint16) (y1 + 0.5 + (min_clip_x - x1)
					* (y2 - y1) / (x2 - x1));	
		} 

		break;

	default:
		break;

	} // switch

	// determine clip point for p2
	switch (p2_code)
	{
	case CLIP_CODE_C: 
		break;

	case CLIP_CODE_N:
		yc2 = (Uint16) min_clip_y;
		xc2 = (Uint16) (x2 + (min_clip_y - y2) * (x1 - x2) / (y1 - y2));
		break;

	case CLIP_CODE_S:
		yc2 = (Uint16) max_clip_y;
		xc2 = (Uint16) (x2 + (max_clip_y - y2) * (x1 - x2) / (y1 - y2));
		break;

	case CLIP_CODE_W:
		xc2 = (Uint16) min_clip_x;
		yc2 = (Uint16) (y2 + (min_clip_x - x2) * (y1 - y2) / (x1 - x2));
		break;

	case CLIP_CODE_E:
		xc2 = (Uint16) max_clip_x;
		yc2 = (Uint16) (y2 + (max_clip_x - x2) * (y1 - y2) / (x1 - x2));
		break;

	// these cases are more complex, must compute 2 intersections
	case CLIP_CODE_NE:
		// north hline intersection
		yc2 = (Uint16) min_clip_y;
		xc2 = (Uint16) (x2 + 0.5 + (min_clip_y - y2) 
				* (x1 - x2) / (y1 - y2));

		// test if intersection is valid, of so then done, else compute next
		if (xc2 < min_clip_x || xc2 > max_clip_x)
		{
			// east vline intersection
			xc2 = (Uint16) max_clip_x;
			yc2 = (Uint16) (y2 + 0.5 + (max_clip_x - x2) 
					* (y1 - y2) / (x1 - x2));
		} 

		break;

	case CLIP_CODE_SE:
		// south hline intersection
		yc2 = (Uint16) max_clip_y;
		xc2 = (Uint16) (x2 + 0.5 + (max_clip_y - y2) 
				* (x1 - x2) / (y1 - y2));	

		// test if intersection is valid, of so then done, else compute next
		if (xc2 < min_clip_x || xc2 > max_clip_x)
		{
			// east vline intersection
			xc2 = (Uint16) max_clip_x;
			yc2 = (Uint16) (y2 + 0.5 + (max_clip_x - x2)
					* (y1 - y2) / (x1 - x2));
		} 

		break;

	case CLIP_CODE_NW:
		// north hline intersection
		yc2 = (Uint16) min_clip_y;
		xc2 = (Uint16) (x2 + 0.5 + (min_clip_y - y2)
				* (x1 - x2) / (y1 - y2));

		// test if intersection is valid, of so then done, else compute next
		if (xc2 < min_clip_x || xc2 > max_clip_x)
		{
			xc2 = (Uint16) min_clip_x;
			yc2 = (Uint16) (y2 + 0.5 + (min_clip_x - x2)
					* (y1 - y2) / (x1 - x2));	
		} 

		break;

	case CLIP_CODE_SW:
		// south hline intersection
		yc2 = (Uint16) max_clip_y;
		xc2 = (Uint16) (x2 + 0.5 + (max_clip_y - y2) 
				* (x1 - x2) / (y1 - y2));	
 
		// test if intersection is valid, of so then done, else compute next
		if (xc2 < min_clip_x || xc2 > max_clip_x)
		{
			xc2 = (Uint16) min_clip_x;
			yc2 = (Uint16) (y2 + 0.5 + (min_clip_x - x2)
					* (y1 - y2) / (x1 - x2));	
		} 

		break;

	default:
		break;

	} // switch

	// do bounds check
	if ((xc1 < min_clip_x) || (xc1 > max_clip_x) ||
	    (yc1 < min_clip_y) || (yc1 > max_clip_y) ||
	    (xc2 < min_clip_x) || (xc2 > max_clip_x) ||
	    (yc2 < min_clip_y) || (yc2 > max_clip_y) )
	{
		return 0;
	} 

	// store vars back
	x1 = xc1;
	y1 = yc1;
	x2 = xc2;
	y2 = yc2;

	return 1;

#undef CLIP_CODE_C  
#undef CLIP_CODE_N 
#undef CLIP_CODE_S 
#undef CLIP_CODE_E 
#undef CLIP_CODE_W 
#undef CLIP_CODE_NE 
#undef CLIP_CODE_SE 
#undef CLIP_CODE_NW 
#undef CLIP_CODE_SW 
}

void 
Painter::drawLine (Uint16 x, Uint16 y, Uint16 x2, Uint16 y2, Uint32 color)
{
	if (y == y2) 
	{
		Uint16 w;
		x > x2 ? w = x - x2 + 1, x = x2 : w = x2 - x + 1;
		return drawHLine (x, y, w, color);
	}
	else if (x == x2)
	{
		Uint16 h;
		y > y2 ? h = y - y2 + 1, y = y2 : h = y2 - y + 1;
		return drawVLine (x, y, h, color);
	}

	if (m_clipping)
	{
		if (!clipLine (x, y, x2, y2, m_clip_rect.x, m_clip_x2,
			       m_clip_rect.y, m_clip_y2))
			return;
	}			
			  
	m_gfx->drawLine (m_target_surface, x, y, x2, y2, color);
}

void
Painter::drawRect (Uint16 x, Uint16 y, Uint16 w, Uint16 h, Uint32 color)
{
	if (w == 0 || h == 0)
		return;

	if (w == 1 && h == 1)
		return putPixel (x, y, color);

	if (h == 1)
		return drawHLine (x, y, w, color);
		
	if (w == 1)
		return drawVLine (x, y, h, color);

	drawHLine (x, y, w, color);
	drawHLine (x, y + h - 1, w, color);

	if (h > 2)
	{
		drawVLine (x, y + 1, h - 2, color);
		drawVLine (x + w - 1, y + 1, h - 2, color);
	}
}

void
Painter::fillRect (Uint16 x, Uint16 y, Uint16 w, Uint16 h, Uint32 color)
{
	Uint16 _x;
	Uint16 _y;
	Uint16 _w;
	Uint16 _h;

#define ___MIN(a,b) (a < b ? a : b)
#define ___MAX(a,b) (a > b ? a : b)

	if (m_clipping)
	{
		if (x > m_clip_x2 || y > m_clip_y2)
			return;

		if (x + w < m_clip_rect.x + 1 ||
		    y + h < m_clip_rect.y + 1)			
			return;
	
		_x = ___MAX (x, m_clip_rect.x);
		_y = ___MAX (y, m_clip_rect.y);
		_w = ___MIN (x + w - 1, m_clip_x2) - _x + 1;
		_h = ___MIN (y + h - 1, m_clip_y2) - _y + 1;
	}
	else
	{
		_x = x;
		_y = y;
		_w = w;
		_h = h;
	}		

#undef ___MIN
#undef ___MAX

	if (_w == 0 || _h == 0)
		return;
	
	if (_w == 1 && h == 1)
		return m_gfx->putPixel (m_target_surface, _x, _y, color);
		
	if (_h == 1)
		return m_gfx->drawHLine (m_target_surface, _x, _y, _w, color);
		
	if (_w == 1)
		return m_gfx->drawVLine (m_target_surface, _x, _y, _h, color);
		
	Uint16 i;
	
	if (_h > _w)
	{
		for (i = 0; i < _w; ++i)
		{
			m_gfx->drawVLine (m_target_surface, _x + i, _y, _h,
					  color);
		}
	}
	else
	{
		for (i = 0; i < _h; ++i)
		{
			m_gfx->drawHLine (m_target_surface, _x, _y + i, _w,
					  color);
		}
	}
}


} // namespace SEL
